package hk.polyu.dds;


import java.math.BigInteger;
import java.net.*;
import java.util.*;

import android.hardware.*;
import android.location.*;
import android.net.*;
import android.net.wifi.*;
import android.net.wifi.p2p.*;
import android.net.wifi.p2p.WifiP2pManager.*;
import android.os.*;
import android.preference.*;
import android.app.*;
import android.content.*;
import android.util.Log;
import android.view.*;
import android.view.View.*;
import android.widget.*;

public class MainUI extends Activity implements 
	SensorEventListener, LocationListener, ChannelListener, PeerListListener, ConnectionInfoListener, GroupInfoListener {
	// manager
	private SensorManager 	mSensorManager;
	private WifiP2pManager 	mWifiP2pManager;
	private LocationManager mLocationManager;
	private WifiManager		mWifiManager;
	
	private Sensor 			mAccelerometer;
	private Sensor			mGyroscope;
	
	private Channel 		mChannel;
	private WifiP2pInfo 	mInfo;
	
    private static long mSmpCnt = 0;
	
	// Server data structure
	boolean		mIsSever = false;
	DataServer 	mDataServer = null;
	DataClient	mDataClient = null;

	Logger 		mLog = null;
	
	// UI
	Button mBtnStart, mBtnStop;
	TextView mTvAccDisp, mTvGyroDisp, mTvLocDisp;
	TextView mTvClientDisp, mTvServerDisp;
	
	static final int MSG_CLIENTCONNECTED = 0x101;
	static final int MSG_STARTSAMPLE = 0x102;
	static final int MSG_STOPSAMPLE = 0x103;
	
	// This method run in main thread.
	Handler mHandler = new Handler() {
		public void handleMessage(Message msg) {   
            switch (msg.what) {   
                 case MSG_CLIENTCONNECTED:
                	 
                	 Log.d("polyu", "Client Connected");
                	 
                     // update UI
                	 HashMap<String, Socket> c = mDataServer.getClient();
                	 String disp = String.format("%d clients.", c.keySet().size());
                	 
                	 for(String s: c.keySet()) {
                		 disp += s + "\r\n";
                	 }
                	 
                	 mTvServerDisp.setText(disp);
                     break;
                 case MSG_STARTSAMPLE:
                	 startSample(0);
                	 break;
                 case MSG_STOPSAMPLE:
                	 stopSample();
                	 break;
            }   
            super.handleMessage(msg);   
       }   
	};

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main_ui);
        
        // strict mode  
        StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
        StrictMode.setThreadPolicy(policy);
        
        // Manage Init
        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        mWifiP2pManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
        mWifiManager = (WifiManager)getSystemService(Context.WIFI_SERVICE);
        mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        
        // Sensor Init
        mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        mGyroscope = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
        mChannel = mWifiP2pManager.initialize(this, this.getMainLooper(), this);
        
        // UI
        mTvAccDisp = (TextView) this.findViewById(R.id.tvAcc);
        mTvGyroDisp = (TextView) this.findViewById(R.id.tvGyro);
        mTvLocDisp = (TextView) this.findViewById(R.id.tvLocation);
        mTvClientDisp = (TextView) this.findViewById(R.id.tvClient);
        mTvServerDisp = (TextView) this.findViewById(R.id.tvServer);
        this.setTitle("Sync Logger");
        
        mBtnStart = (Button) this.findViewById(R.id.btnStart);
        mBtnStart.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				
				mTvClientDisp.setText("Connecting to server...");
				setupClient();
				
				if ( mIsSever) {
					// wait for 1 secs
					try {
						Thread.sleep(500);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					
					// ask weather to start.
					AlertDialog.Builder builder = new AlertDialog.Builder(MainUI.this);
			    	builder
			    	.setTitle("Data Collection")
			    	.setMessage("Ready to send data collection request?")
			    	.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
			    	    public void onClick(DialogInterface dialog, int which) {			      	
			    	    	//Yes button clicked, do something
			    	    	mDataServer.sendCollectionRequest();
			    	    }
			    	})
			    	.setNegativeButton("No", null)						//Do nothing on no
			    	.show();
				}
					
			}
		});
        
        // Stop button
        mBtnStop = (Button) this.findViewById(R.id.btnStop);
        mBtnStop.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				
				if (mIsSever) {
					mDataServer.sendStopRequest();
				}
				
			}
		});

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main_ui, menu);
        return true;
    }

    /*
	@Override
	protected void onPause() {
		mSensorManager.unregisterListener(this);
		super.onPause();
	}
	@Override
	protected void onResume() {
	}
	*/
    
    public void searchPeer(){

		mWifiP2pManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
            @Override
            public void onSuccess() { }

            @Override
            public void onFailure(int reasonCode) {
                Toast.makeText(MainUI.this, "Discovery Failed: " + reasonCode, Toast.LENGTH_SHORT).show();
            }
        });
		
    }
    
    public static byte byteOfInt(int value, int which) {
        int shift = which * 8;
        return (byte)(value >> shift); 
      }
    
    public static InetAddress intToInet(int value) {
        byte[] bytes = new byte[4];
        for(int i = 0; i<4; i++) {
          bytes[i] = byteOfInt(value, i);
        }
        try {
          return InetAddress.getByAddress(bytes);
        } catch (UnknownHostException e) {
          // This only happens if the byte array has a bad length
          return null;
        }
      }
    
    protected void setupServer() {
    	
    	mDataServer = new DataServer(this.mHandler);
    	mDataServer.start();
    	
    	this.mTvServerDisp.setText("Waiting for client connection.");
    }
    
    public String getLocalIpAddress() {
        try {
            for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements();) {
                NetworkInterface intf = en.nextElement();
                for (Enumeration<InetAddress> enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements();) {
                    InetAddress inetAddress = enumIpAddr.nextElement();
                    if (!inetAddress.isLoopbackAddress()) {
                        Log.d("IPs", inetAddress.getHostAddress() );
                    }
                }
            }
        } catch (SocketException ex) {
            Log.e("polyu", ex.toString());
        }
        return null;
    }
    
    protected void setupClient() {
    	
		SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
    	
    	InetAddress addr = null;
    	
    	if (sharedPrefs.getBoolean("phylayer", false) == false) {
    		// Wifi Direct
    		addr = this.mInfo.groupOwnerAddress;
    	} else {
    		// wifi hotspot, fixed address.
   			try {
				addr = InetAddress.getByName("192.168.43.1");
			} catch (UnknownHostException e) {
				e.printStackTrace();
			}
    	}
    	
    	Log.d("polyu", "Client connecting to : " + addr.toString());

    	this.mDataClient = new DataClient(this.mHandler, addr);
    	this.mDataClient.start();
    	this.mTvClientDisp.setText("Server Connected.");

    }
    
    
    public void startSample(int mode) {
    	
    	mSmpCnt = 0;
    	
		String [] list = new String[1];
		list[0] = "localhost";

		try {
			mLog = new Logger(
					getApplicationContext(), 
					new Date(),
					list);
			
			mLog.open();
			} catch (Exception e) {
				e.printStackTrace();
			}

		mSensorManager.registerListener(MainUI.this, mAccelerometer, SensorManager.SENSOR_DELAY_FASTEST);
		mSensorManager.registerListener(MainUI.this, mGyroscope, SensorManager.SENSOR_DELAY_FASTEST);
		
		if (this.mIsSever) {
			mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10000, 5,	this, Looper.getMainLooper());
		}
		
		mBtnStop.setEnabled(true);
		mBtnStart.setEnabled(false);
		
		this.mTvClientDisp.setText("Sample Started");
		Log.d("polyu", "Sample Started");
    }
    
    public void stopSample() {
    	
		mSensorManager.unregisterListener(MainUI.this);
		if (this.mIsSever) {
			mLocationManager.removeUpdates(this);
		}
    	
    	try {
    		if(mLog != null) {
    			mLog.close();
    			mLog = null;
    		}
		} catch(Exception e) {
			e.printStackTrace();
		}
		
		mBtnStop.setEnabled(false);
		mBtnStart.setEnabled(true);
		
		Log.d("polyu", "Sample Stoped");
		this.mTvClientDisp.setText("Sample Stopped");
    }
    

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {

		switch(item.getItemId()) {
		case R.id.menu_scan:
			// whether cooperative sensing or not.
			SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(MainUI.this);
			if (sharedPrefs.getBoolean("coop", false) == false) 
				return true;
			
			if (sharedPrefs.getBoolean("phylayer", false) == false) {
				// Wifi Direct
				this.mWifiP2pManager.requestConnectionInfo(this.mChannel, this);
			} else {
				// print my address
				getLocalIpAddress();
				
				// Wi-Fi Hot spot
				if (sharedPrefs.getBoolean("role", false) == true) {
					this.mIsSever = true;
					this.setupServer();
				} else {
					// Do nothing for wifi client, assume it has been connected.
				}
			}
			
			break;
		case R.id.menu_settings:
			this.startActivity(new Intent(MainUI.this, SettingActivity.class));
			break;
		case R.id.menu_touch:
			this.startActivity(new Intent(MainUI.this, TouchLogger.class));
			break;
		case R.id.menu_exit:
			this.finish();
			break;
		}
		return super.onOptionsItemSelected(item);
	}

	@Override
	public void onChannelDisconnected() {
		Log.d("polyu", "onChannelDisconnected");
		Toast.makeText(MainUI.this, "onChannelDisconnected", Toast.LENGTH_SHORT).show();	
	}
	
	@Override
	public void onGroupInfoAvailable(WifiP2pGroup arg0) {
		
	}
	

	@Override
	public void onPeersAvailable(WifiP2pDeviceList peerList) {
		
//		if (mProgressDialog != null && mProgressDialog.isShowing()) {
//			mProgressDialog.dismiss();
//        }
//		
//		Log.d("polyu", "Discover completed");
//		mPeers.clear();
//		mPeers.addAll(peerList.getDeviceList());
//        
//		Toast.makeText(MainUI.this, "Discover Complete: " + mPeers.size(), Toast.LENGTH_SHORT).show();
//		
//		this.setupClient();
	}

	@Override
	public void onConnectionInfoAvailable(WifiP2pInfo info) {
		this.mInfo = info;
		
		if(this.mInfo.isGroupOwner) {
			this.mIsSever = true;
			String disp = String.format("Group owner with address: %s", info.groupOwnerAddress.toString());
			this.mTvServerDisp.setText(disp);
			
			// setup server here.
			this.setupServer();
		} else {
			this.mIsSever = false;
			String disp = String.format("This is a client, Click Start to connect to server");
			this.mTvClientDisp.setText(disp);
		}
		
	}
	

    @Override
	public void onAccuracyChanged(Sensor arg0, int arg1) {
	}

    @Override
	public void onSensorChanged(SensorEvent event) {
		
    	int type = event.sensor.getType();
    	float [] data = event.values.clone();
  
    	//String disp = String.format("x=%+.3f,\ty=%+.3f,\tz=%+.3f", data[0], data[1], data[2]);
    	if (this.mLog != null) {
    		this.mLog.append("localhost", type, data);
    		mSmpCnt++;
    		if (mSmpCnt % 1000 == 0) {
    			this.mTvClientDisp.setText("Logging items: " + mSmpCnt);
    		}
    	}
	}

	@Override
	public void onLocationChanged(Location loc) {
		float [] data  = new float[3];
		data[0] = (float)loc.getLatitude();
		data[1] = (float)loc.getLongitude();
		data[2] = (float)loc.getAltitude();
		
		this.mLog.append("localhost", 0, data);
	}

	@Override
	public void onProviderDisabled(String arg0) {}

	@Override
	public void onProviderEnabled(String arg0) {}

	@Override
	public void onStatusChanged(String arg0, int arg1, Bundle arg2) {}

	@Override
	protected void onDestroy() {
		if (this.mDataClient != null) {
			this.mDataClient.stopClient();
		}
		
		if (this.mIsSever && this.mDataServer != null) {
			this.mDataServer.stopServer();
		}
		
		super.onDestroy();
	}
    
}
